
<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

    <title>Leo University &#8212; Leo 6.7.2 documentation</title>
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
    <link rel="stylesheet" type="text/css" href="_static/classic.css" />
    <link rel="stylesheet" type="text/css" href="_static/custom.css" />
    
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
    <script src="_static/jquery.js"></script>
    <script src="_static/underscore.js"></script>
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
    <script src="_static/doctools.js"></script>
    <script src="_static/sphinx_highlight.js"></script>
    
    <script src="_static/sidebar.js"></script>
    
    <link rel="index" title="Index" href="genindex.html" />
    <link rel="search" title="Search" href="search.html" />
    <link rel="next" title="Appendices" href="appendices.html" />
    <link rel="prev" title="The Leonine World" href="leonine-world.html" /> 
  </head><body>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="appendices.html" title="Appendices"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="leonine-world.html" title="The Leonine World"
             accesskey="P">previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" accesskey="U">Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">Leo University</a></li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body" role="main">
            
  <section id="leo-university">
<h1>Leo University<a class="headerlink" href="#leo-university" title="Permalink to this heading">¶</a></h1>
<p>This is a backup of the online <a class="reference external" href="https://github.com/leo-editor/leo-editor/issues/816">LeoU issues</a>.</p>
<section id="lesson-1-think-process-not-knowledge">
<h2>Lesson 1: Think process, not knowledge<a class="headerlink" href="#lesson-1-think-process-not-knowledge" title="Permalink to this heading">¶</a></h2>
<p>Programming is <em>much</em> easier than you might think.</p>
<p>Many of Leo’s most important devs made major contributions to Leo without asking me a single question.  Do they have super powers?  No, they don’t.</p>
<p>But they do know the secret of programming: <strong>Simple processes, endlessly repeated, suffice to find, understand and change *any* software</strong>.  And Leo simplifies these techniques even further.  Here they are…</p>
<p><strong>Basic skill/process 1: Familiarize yourself with the code</strong></p>
<p>For any project you want to know what files exist.  When studying other people’s code I use a recursive import script <a class="reference external" href="https://github.com/leo-editor/leo-editor/issues/846">https://github.com/leo-editor/leo-editor/issues/846</a> to create a tree of files. The goal is just to have a <em>vague</em> understanding of what files are involved. Don’t even think about remember details.</p>
<p>It’s even easier with Leo.  LeoPy.py contains Leo’s sources.  Peruse this outline, seeing what <cite>&#64;file</cite> nodes it contains.</p>
<p><em>Don’t make a big deal about this step</em>. It should just take 5 or 10 minutes. Look <em>only</em> at files that interest you. In those files you will probably want to look briefly at the top-level classes in the files that interest you. You will likely repeat this step many times, adding a little to your understanding each time.</p>
<p><strong>Basic skill/process 2: Learn what to search for</strong></p>
<p>We devs often want to change how commands work.  You can usually find the top-level code for a command simply by searching for the command’s name, in single quotes.</p>
<p>To find the definition of a method, function or class, simply control-click on it.  That will start a search for def x and class x.  If that doesn’t work, a cff (clone-find-all-flattened) search on a name will find all uses of that name in the present outline.</p>
<p><strong>Basic skill/process 3: Look for helpers in child nodes</strong></p>
<p>With Leo outlines <strong>it’s natural to put a method’s helpers in child nodes</strong>. I usually forget implementation details, but the children of a node usually contain most of the details.</p>
<p><strong>Basic skill/process 4: Use clones to organize and remember your work</strong></p>
<p>When working on a project I make create an organizer node as the last top-level node of the outline.  This node will typically contain a link to the github issue.  I make clones of all relevant nodes and move them so they are clones of the organizer node.  For complex projects I may create subsidiary organizer nodes, like “changed”, “reference”, etc.</p>
<p>When a project is done (or suspended), I move the project’s organizer node so it is the last child of <em>another</em> organizer node called “Recent Code”. This hides all work except the what I am, this minute, working on.  This is important: it hides details and lessens distractions.</p>
<p><strong>Summary</strong></p>
<p>The easy processes just described form the basis for programming workflow.</p>
<p><em>Expect to repeat these processes many times</em>.  Don’t worry if not everything is clear at first.</p>
</section>
<section id="lesson-2-learn-leo-s-debugging-tools">
<h2>Lesson 2: Learn Leo’s debugging tools<a class="headerlink" href="#lesson-2-learn-leo-s-debugging-tools" title="Permalink to this heading">¶</a></h2>
<p>Here are the basics of debugging with Leo:</p>
<p><strong>g.trace</strong></p>
<p><cite>g.trace(*args, **kwargs)</cite> prints its arguments to the console.  I use g.trace all throughout Leo’s code base.</p>
<p><strong>g.pdb</strong></p>
<p><cite>g.pdb()</cite> invokes python’s pdb debugger, <a class="reference external" href="https://docs.python.org/3/library/pdb.html">https://docs.python.org/3/library/pdb.html</a>, adapted to Leo’s Qt gui.  I use g.pdb when g.trace doesn’t tell me enough.</p>
</section>
<section id="lesson-3-running-unit-tests">
<h2>Lesson 3: Running unit tests<a class="headerlink" href="#lesson-3-running-unit-tests" title="Permalink to this heading">¶</a></h2>
<p>​All devs must run all unit tests before commit anything. Here’s how:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cd</span> <span class="o">&lt;</span><span class="n">path</span> <span class="n">to</span><span class="o">&gt;</span><span class="n">leo</span><span class="o">-</span><span class="n">editor</span>
<span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">unittest</span> <span class="o">%*</span>
</pre></div>
</div>
<p><strong>Important</strong>: Some unit tests may fail on your environment.  Don’t worry about them.  You need only ensure that your new code causes no <em>new</em> unit tests to fail.  If in doubt, ask on leo-editor.</p>
</section>
<section id="lesson-4-use-issues-as-an-programming-notebook">
<h2>Lesson 4: Use issues as an programming notebook<a class="headerlink" href="#lesson-4-use-issues-as-an-programming-notebook" title="Permalink to this heading">¶</a></h2>
<p>I recommend using github issues to track your work. This is especially true for Leo devs.  You want other people to be able to follow and comment on your work.</p>
<p>The convention is to have the first comment for any issue contain an up-to-date summary of the status of the issue.  I have found that it is useful to continually edit the first comment.</p>
</section>
<section id="lesson-5-c-recursiveimport-imports-files-recursively">
<h2>Lesson 5: c.recursiveImport imports files recursively<a class="headerlink" href="#lesson-5-c-recursiveimport-imports-files-recursively" title="Permalink to this heading">¶</a></h2>
<p>c.recursiveImport is a wrapper on the RecursiveImportController class.​ ​You can find the related code by doing a regex cff (ignoring case) on ​ ​“recursive?import”.</p>
<p>Here is how to use c.recursiveImport:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span><span class="o">.</span><span class="n">recursiveImport</span><span class="p">(</span>
    <span class="n">dir_</span> <span class="o">=</span> <span class="o">&lt;&lt;</span> <span class="n">path</span> <span class="n">to</span> <span class="n">root</span> <span class="n">directory</span> <span class="o">&gt;&gt;</span><span class="p">,</span>
        <span class="c1"># You must expand ~ yourself</span>
    <span class="n">add_path</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
    <span class="n">kind</span> <span class="o">=</span> <span class="s1">&#39;@clean&#39;</span><span class="p">,</span> <span class="c1"># &#39;@clean&#39;, @nosent&#39;,&#39;@auto&#39;,&#39;@file&#39;</span>
    <span class="n">recursive</span> <span class="o">=</span> <span class="kc">True</span><span class="p">,</span>
    <span class="n">safe_at_file</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>
    <span class="n">theTypes</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;.py&#39;</span><span class="p">,],</span>
<span class="p">)</span>
</pre></div>
</div>
<p>I like to import to &#64;clean and set safe_at_file = False, but <em>before doing that</em> I create a git repo for the to-be-imported directory and check in all the .py files. That way I can track any changes that get made, and revert them if need be.</p>
</section>
<section id="lesson-6-how-devs-should-use-git-branches">
<h2>Lesson 6: How devs should use git branches<a class="headerlink" href="#lesson-6-how-devs-should-use-git-branches" title="Permalink to this heading">¶</a></h2>
<p>Marcel Franke showed how to use the recommended way of using git branches:
<a class="reference external" href="https://groups.google.com/d/msg/leo-editor/p_6ljMQL660/57Z8lYuQAAAJ">https://groups.google.com/d/msg/leo-editor/p_6ljMQL660/57Z8lYuQAAAJ</a></p>
<p>Here are his remarks, slightly edited:</p>
<ul class="simple">
<li><p><strong>master</strong> is stable.</p></li>
<li><p><strong>devel</strong> is “mostly” stable.</p></li>
<li><p><strong>feature branches</strong> off devel can be wildly unstable.</p></li>
<li><p><strong>release branches</strong> are candidates for master, so should be stable.</p></li>
</ul>
<p>Everything new comes from devel and goes to devel. Devs <strong>must</strong> run unit tests before pushing to devel, so devel should usually be stable.</p>
<p>All experimental code should reside in a feature branch.</p>
<p>At all times, anyone can pull stable code from master.</p>
<p>Only the maintainer (EKR) is supposed to work on release or master, so nobody needs to hold back or knows what is going on in them. At all times everyone can happily hack on devel,  or better, their personal feature branches.</p>
<p>To prepare a new release,  the maintainer branches devel to a new branch, say 6.x.y.  The maintainer runs tests on it, and makes any last-minute fixes. To publish the release, the maintainer merges the release to master <strong>and</strong> to devel, then optionally deletes the release branch.</p>
<p>This visualization made it all clear to me:
<a class="reference external" href="https://blog.seibert-media.net/wp-content/uploads/2014/03/Gitflow-Workflow-3.png">https://blog.seibert-media.net/wp-content/uploads/2014/03/Gitflow-Workflow-3.png</a></p>
</section>
<section id="lesson-7-how-to-make-complex-changes-to-a-code-base">
<h2>Lesson 7: How to make complex changes to a code base<a class="headerlink" href="#lesson-7-how-to-make-complex-changes-to-a-code-base" title="Permalink to this heading">¶</a></h2>
<dl class="simple">
<dt>This lesson discusses the <em>process</em> of making arbitrarily complex changes throughout a code base.</dt><dd><p>It’s an elaboration of Lesson 1: Think process, not knowledge, but it’s worth reading because it contains helpful hints about tackling <em>any</em> big project.  I used this general method when making Leo’s code base work with Python 3 as well as Python 2.</p>
</dd>
</dl>
<p>The grand summary:</p>
<ol class="arabic simple">
<li><p>Study the code first with clone-find commands.</p></li>
<li><p>Let python crashes &amp; failed unit tests guide you.</p></li>
</ol>
<p>In detail:</p>
<ol class="arabic simple">
<li><p>Create a new git branch for the work.</p></li>
<li><p>Create a <strong>global master switch**that separates and marks the old and new versions of the code. This is a boolean.  It selects the new code if True and the old code otherwise.  We say the switch is either in the “**new</strong>” position or the “<strong>legacy</strong>” position.</p></li>
</ol>
<ul class="simple">
<li><p><strong>All unit tests should continue to pass when the switch selects legacy code.</strong></p></li>
<li><p><strong>All pushes should have the legacy code selected until every works.</strong></p></li>
</ul>
<p>These two requirements make it safe to pause work on the project.</p>
<ol class="arabic simple" start="3">
<li><p>Make sure you understand what needs to be done.  Use the clone find commands to answer any and all questions about the code and how it should be changed.</p></li>
<li><p>Make the fundamental changes required, <strong>without worrying about the consequences</strong>.</p></li>
</ol>
<p>For the “keys” branch I changed the representation of settings so that they are NewLeoKey objects.</p>
<ol class="arabic simple" start="5">
<li><p>With the switch in the “new” position, run a test copy of Leo.</p></li>
</ol>
<p>Because of step 4, we <em>expect</em> crashes.  Fix each as they happen, separating the old and new code with a test on the switch.  Repeat this step until no crashes happen.</p>
<p>When this phase works, set the switch to “legacy”, re-run all unit tests, and commit the code.</p>
<ol class="arabic simple" start="6">
<li><p>For the “keys” branch, getting through startup is only half the task.  When I type a key I <em>expect</em> more crashes.  I’ll follow step 5 until no crashes happen when I type various key combination.</p></li>
</ol>
<p>Again, I’ll set the switch to “legacy”, re-run all unit tests and commit the code.</p>
<ol class="arabic simple" start="7">
<li><p>When normal operation appears to work with the switch in the “new” position, <strong>run all unit tests</strong> and fix any crashes or unit tests failures.</p></li>
</ol>
<p>Now you can commit the code with the switch in the “new” position.</p>
<ol class="arabic simple" start="8">
<li><p>At this point you can start eating your own dog food.  When satisfied that the code is stable enough, you can do the following:</p></li>
</ol>
<ul class="simple">
<li><p>Remove the switch and all code disabled when the switch is in the “legacy” position.</p></li>
<li><p>Run all unit tests.</p></li>
<li><p>Continue to eat your own dog food.</p></li>
<li><p>Commit the branch to “devel” when all seems well.</p></li>
</ul>
<p>That’s the process.  It’s extremely effective.  You don’t have to remember your changes because a cff on the switch should yield <em>all</em> changed code.  Naturally, you can make clones of nodes containing the most important changes.</p>
</section>
<section id="leou-suggestions-for-theme-developers">
<h2>LeoU: Suggestions for theme developers<a class="headerlink" href="#leou-suggestions-for-theme-developers" title="Permalink to this heading">¶</a></h2>
<p>I recommend using the organization of leo/themes/EKRDark.leo as a guide. <strong>Note</strong>: Syntax coloring settings are required because they are <em>not</em> used in the css.</p>
<p>To avoid confusion, it’s best if theme .leo files contain no <strong>unbound settings</strong>. This will ensure that the appearance of a theme does not unwittingly depend on settings in myLeoSettings.leo.</p>
<p><strong>Important</strong>: Theme devs should set the following <strong>theme description settings</strong>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@string</span> <span class="n">theme_name</span>   <span class="c1"># crucial</span>
<span class="nd">@bool</span> <span class="n">color_theme_is_dark</span>
<span class="nd">@string</span> <span class="n">color_theme</span> <span class="o">=</span> <span class="n">ekr_dark</span>
</pre></div>
</div>
<p>These settings ensure that relative urls (including “:/” syntax are resolved properly in css, and that icons are found in the proper directories.</p>
<p><strong>Icons boxes should have constant width</strong>.  Otherwise, the variable indentation of the rest of the headline appears to represent variable outline structure, which is highly confusing.</p>
</section>
<section id="leou-about-leo-s-architecture">
<h2>LeoU: About Leo’s architecture<a class="headerlink" href="#leou-about-leo-s-architecture" title="Permalink to this heading">¶</a></h2>
<p>By the term architecture I mean the design of Leo’s modules and classes.</p>
<p><strong>Information hiding</strong></p>
<p>Leo’s design is based on the thinking of Glenford Meyers. Let’s use the word component to mean either a Python module or class.</p>
<p>We devs must be able to change the implementation of a component without affecting any other code. This has been true throughout Leo’s 20+ years of history. This is the foundation on which Leo is built.</p>
<p><strong>Principle 1</strong>: Each class should concern itself with one, easily defined “area of concern”.</p>
<p>Modules should contain related classes. All components should have short, simple descriptions, no matter how complex they are internally.</p>
<p><strong>Principle 2</strong>: Components should know as little as possible about the internal details of other components.</p>
<p>Components have well-defined boundaries. The inside of a component contains the implementation. The outside of a component is its API.</p>
<p>Changes to the API can break code. However, knowing about the existence of a component’s high-level methods does not (usually) blur the boundaries of a component!</p>
<p>In most cases, components should know nothing about the ivars and low-level methods. However, components can freely use the so-called official ivars of other classes. These official ivars are highly unlikely ever to change. And they are useful. We wouldn’t want to require long-winded alternatives to, say, c.frame.body.wrapper.</p>
<p><strong>Class design: inheritance vs instantiation</strong></p>
<p>Leo uses the “has-a” pattern, rather than the “is-a” class pattern. That is, Leo gains access to the capabilities of other classes by instantiating the other classes rather than being subclasses of other classes. The “has-a” pattern is a personal preference of mine, and is now pretty much baked into Leo.</p>
<p>The “has-a” pattern completely insulates classes from the internal details of other classes. There is no need to worry about clashing ivars, or _ ivars, etc. Otoh, the “has-a” pattern results in more complex startup code. Imo, that “extra” complexity is well worthwhile.</p>
</section>
<section id="leou-about-leo-s-position-and-vnode-classes">
<h2>LeoU: About Leo’s Position and VNode classes<a class="headerlink" href="#leou-about-leo-s-position-and-vnode-classes" title="Permalink to this heading">¶</a></h2>
<p>From this post: <a class="reference external" href="https://groups.google.com/forum/#!topic/leo-editor/SjHfXepxiTo">https://groups.google.com/forum/#!topic/leo-editor/SjHfXepxiTo</a>
Not everyone agrees with everything I say here.</p>
<p>This LeoU discussion discusses the present code base only. #883 (a low priority item) discusses ways in which the tree of vnodes could be decoupled from c, the Commands instance.</p>
<p><strong>The Position class</strong></p>
<p>The Position class concerns itself only with traversing the tree. The position class contains no node-related data.  In order to insert top-level nodes, methods of the Position class gets access to c.hiddenRootNode via p.v.context.</p>
<p><strong>The VNode class</strong></p>
<p>The VNode class contains all data in an outline, as well as crucial, low-level operations on those data. There is no other place to store node-related data.</p>
<p>The VNode class is one of the foundations of Leo’s scripting. As a result, <em>none</em> of the ivars or methods of the VNode class can change in any way. That is, all of the ivars of the VNode class are “official”. Scripts can, and do, access these ivars directly. Perhaps the “context” kwarg of <cite>VNode.__init__</cite> should just be called “c”, but that can’t be done now.</p>
<p>Theoretically, it would be possible to add new kwargs to VNode methods, but that is extremely unlikely. It would also be possible to add new ivars to the VNode class, but uA’s make that unnecessary.</p>
<p>The properties of the VNode class, v.b, v.h, v.gnx, v.u, allow higher-level access to various VNode ivars, but in some cases Leo’s code accesses the corresponding ivars directly, for greater performance.</p>
<p><strong>Interaction with other classes</strong></p>
<p>The v.context ivar is controversial. In fact, the v.context ivar is completely benign. It has never caused problems, and it never will. It does not, in any significant way, make the VNode class dependent on any other class. It has no harmful architectural consequences.</p>
<p>Yes, the FileCommands class must know about gnx’s, but <em>the converse is not true</em>.  The VNode class knows nothing about how other classes use gnx’s.  The FileCommands class creates VNode instances without knowing <em>anything</em> about low-level VNode methods.</p>
<p>The VNode class is, in fact, an excellent example of information hiding.  The boundary between the VNode class is well defined and well enforced.</p>
<p><strong>Possible improvements</strong></p>
<ol class="arabic">
<li><p>A new c.new_vnode method could help instantiate VNodes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">new_vnode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">gnx</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
    <span class="n">c</span> <span class="o">=</span> <span class="bp">self</span>
    <span class="k">return</span> <span class="n">leoNodes</span><span class="o">.</span><span class="n">VNode</span><span class="p">(</span><span class="n">context</span><span class="o">=</span><span class="n">c</span><span class="p">,</span> <span class="n">gnx</span><span class="o">=</span><span class="n">gnx</span><span class="p">)</span>
</pre></div>
</div>
</li>
</ol>
<p>The (small) advantage is that clients of c.new_vnode(gnx) would no longer need to import leo.core.leoNodes.</p>
<ol class="arabic simple" start="2">
<li><p>The  v.saveCursorAndScroll and v.restoreCursorAndScroll could be moved elsewhere. At present, these methods cause no problems, but moving them would be part of #883.</p></li>
</ol>
</section>
</section>


            <div class="clearer"></div>
          </div>
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
            <p class="logo"><a href="leo_toc.html">
              <img class="logo" src="_static/LeoLogo.svg" alt="Logo"/>
            </a></p>
  <div>
    <h4>Previous topic</h4>
    <p class="topless"><a href="leonine-world.html"
                          title="previous chapter">The Leonine World</a></p>
  </div>
  <div>
    <h4>Next topic</h4>
    <p class="topless"><a href="appendices.html"
                          title="next chapter">Appendices</a></p>
  </div>
<div id="searchbox" style="display: none" role="search">
  <h3 id="searchlabel">Quick search</h3>
    <div class="searchformwrapper">
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
      <input type="submit" value="Go" />
    </form>
    </div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
        </div>
<div id="sidebarbutton" title="Collapse sidebar">
<span>«</span>
</div>

      </div>
      <div class="clearer"></div>
    </div>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="appendices.html" title="Appendices"
             >next</a> |</li>
        <li class="right" >
          <a href="leonine-world.html" title="The Leonine World"
             >previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" >Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">Leo University</a></li> 
      </ul>
    </div>
    <div class="footer" role="contentinfo">
        &#169; Copyright 1997-2023, Edward K. Ream.
      Last updated on February 28, 2023.
      Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 5.3.0.
    </div>
  </body>
</html>